package ar.edu.utn.frba.disenio.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import ar.edu.utn.frba.disenio.fechas.Fecha;
import ar.edu.utn.frba.disenio.fechas.Formato;
import ar.edu.utn.frba.disenio.model.exceptions.NoHayVuelosConEscalaDisponibles;

public class Busqueda {

	Lugar origen, destino;
	Fecha fechaSalida;
	List<Filtro> filtros = new ArrayList<Filtro>();
	List<VueloDirecto> vuelosEncontrados = new ArrayList<VueloDirecto>();
	
	Formato formato = new Formato("dd/MM/yyyy");

	/*Mensajes*/
	public Busqueda(Lugar origen, Lugar destino, Fecha fechaSalida, List<Filtro> filtros) {
		this.origen = origen;
		this.destino = destino;
		this.fechaSalida = fechaSalida;
		if (filtros!=null)	this.filtros = filtros;
	}
	
	public List<VueloDirecto> buscarVuelos(List<Aerolinea> aerolineas, Usuario unUsuario){
		
		List<VueloDirecto> listaDeVuelos = this.getVuelosFiltrados(aerolineas, unUsuario);
		
		this.setVuelosEncontrados(listaDeVuelos);
		unUsuario.agregarBusqueda(this);
		
		return listaDeVuelos;
	}

	public List<VueloEscalado> buscarVuelosEscalados (List<Aerolinea> aerolineas, Usuario unUsuario){
		
		Busqueda busquedaDeOrigen = new Busqueda(this.getOrigen(), null, this.getFechaSalida(), this.getFiltros());
		List<VueloEscalado> listaReturn = buscarHastaLlegar(busquedaDeOrigen.getVuelosFiltrados(aerolineas, unUsuario),new VueloEscalado(),aerolineas,unUsuario);
		List<VueloEscalado> listaFiltrada = this.filtrarVuelosConEscala(listaReturn);
		if (listaFiltrada.isEmpty()){
			throw new NoHayVuelosConEscalaDisponibles();
		} else {
			return listaFiltrada;
		}
	}
	
	public List<Asiento> getAsientosOrdenados(Comparator<Asiento> orden){
		List<Asiento> listaDeAsientos = this.getTodosLosAsientos();
		
		Collections.sort(listaDeAsientos,orden);
		
		return listaDeAsientos;
	}
	
	public List<VueloDirecto> getVuelosOrdenados (Comparator<Vuelo> orden){
		List<VueloDirecto> listaDeVuelos = this.getVuelosEncontrados();
		
		Collections.sort(listaDeVuelos, orden);
		
		return listaDeVuelos;
	}
	
	public List<Asiento> getTodosLosAsientos(){
		List<Asiento> todosLosAsientos = new ArrayList<Asiento>();
		
		for (VueloDirecto unVuelo: this.getVuelosEncontrados()){
			todosLosAsientos.addAll(unVuelo.getAsientos());
		}
		
		return todosLosAsientos;
	}
	
	/*Metodos*/
	private List<Asiento> filtrar(List<Asiento> asientos){
		List<Asiento> asientosFiltrados = asientos;
		
		for(Filtro unFiltro : this.getFiltros()){
			asientosFiltrados = unFiltro.aplicarFiltro(asientosFiltrados);
		}
		
		return asientosFiltrados;
	}
	
	private List<VueloEscalado> filtrarVuelosConEscala(
			List<VueloEscalado> listaVuelosConEscala) {
		List<VueloEscalado> listaReturn = new ArrayList<VueloEscalado>();
		for (VueloEscalado vuelo: listaVuelosConEscala){
				if (vuelo.cantidadEscalas() < 3)
					listaReturn.add(vuelo);
			}
		return listaReturn;
	}

	private List<VueloEscalado> buscarHastaLlegar(List<VueloDirecto> lista,VueloEscalado escaladoVuelo,List<Aerolinea> aerolineas, Usuario unUsuario) {
		
		List<VueloEscalado> vuelosConEscalas = new ArrayList<VueloEscalado>();
		for (VueloDirecto vuelo : lista) {
			escaladoVuelo.agregarVuelo(vuelo);
			if (this.getDestino().mismoLugar(vuelo.getDestino())) {
				vuelosConEscalas.add(escaladoVuelo);
			}
			else {
				Busqueda busqueda = new Busqueda(vuelo.getDestino(), null, vuelo.getFechaLlegada(), this.getFiltros());
				vuelosConEscalas.addAll(this.buscarHastaLlegar(busqueda.getVuelosFiltrados(aerolineas, unUsuario),escaladoVuelo,aerolineas,unUsuario));
			}
		}
		return vuelosConEscalas;
	}
	
	private List<VueloDirecto> filtrarVuelos(List<VueloDirecto> vuelosEncontrados){
		List<VueloDirecto> vuelosFiltrados = new ArrayList<VueloDirecto>();
		
		for(VueloDirecto unVueloEncontrado : vuelosEncontrados){
			List<Asiento> asientosFiltrados = this.filtrar(unVueloEncontrado.getAsientos());
			if(!asientosFiltrados.isEmpty()){
				VueloDirecto vueloFiltrado = unVueloEncontrado;
				vueloFiltrado.setAsientos(asientosFiltrados);
				vuelosFiltrados.add(vueloFiltrado);
			}
		}
		
		return vuelosFiltrados;
	}
	
	/*Getters*/
	public Lugar getOrigen() {
		return origen;
	}

	public Lugar getDestino() {
		return destino;
	}

	public Fecha getFechaSalida() {
		return fechaSalida;
	}

	public Formato getFormato() {
		return formato;
	}

	public List<Filtro> getFiltros() {
		return filtros;
	}

	private void setVuelosEncontrados(List<VueloDirecto> listaDeVuelos) {
		this.vuelosEncontrados = listaDeVuelos;
	}

	public List<VueloDirecto> getVuelosEncontrados() {
		return vuelosEncontrados;
	}
	
	private List<VueloDirecto> getVuelosFiltrados(List<Aerolinea> aerolineas, Usuario unUsuario) {
		List<VueloDirecto> listaDeVuelos = new ArrayList<VueloDirecto>();
		
		for (Aerolinea unaAerolinea : aerolineas) {
				listaDeVuelos.addAll(unaAerolinea.obtenerVuelos(this));
		}
		
		List<VueloDirecto> listaFiltrada = this.filtrarVuelos(listaDeVuelos);
		if (!unUsuario.esVip()) listaFiltrada = this.quitarSuperofertas(listaFiltrada);
		
		return listaFiltrada;
	} 
	
	private List<VueloDirecto> quitarSuperofertas(List<VueloDirecto> vuelosEncontrados) {
		List<VueloDirecto> vuelosFiltrados = new ArrayList<VueloDirecto>();
		
		for(VueloDirecto unVueloEncontrado : vuelosEncontrados){
			List<Asiento> listaAsientosConSuperOfertas = unVueloEncontrado.getAsientos();
			List<Asiento> listaAsientosSinSuperOfertas = new ArrayList<Asiento>();
			VueloDirecto nuevoVuelo = unVueloEncontrado;
			
			for (Asiento unAsiento : listaAsientosConSuperOfertas){
				if (!unAsiento.esSuperOferta()) listaAsientosSinSuperOfertas.add(unAsiento);
			}
			
			if(!listaAsientosSinSuperOfertas.isEmpty()){
				nuevoVuelo.setAsientos(listaAsientosSinSuperOfertas);
				vuelosFiltrados.add(nuevoVuelo);
			}
			
		}
		
		return vuelosFiltrados;
	}
}
